#include <assert.h>
#include "common.h"
#include "tcp_server.h"
#include "thread_pool.h"

int tcp_server(int port)
{
    int listenfd;
    listenfd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(port);

    int on = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    int rt1 = bind(listenfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (rt1 < 0)
    {
        error(1, errno, "bind failed ");
    }

    int rt2 = listen(listenfd, LISTENQ);
    if (rt2 < 0)
    {
        error(1, errno, "listen failed ");
    }

    signal(SIGPIPE, SIG_IGN);

    int connfd;
    struct sockaddr_in client_addr;
    socklen_t client_len = sizeof(client_addr);

    if ((connfd = accept(listenfd, (struct sockaddr *)&client_addr, &client_len)) < 0)
    {
        error(1, errno, "bind failed ");
    }

    return connfd;
}

int tcp_server_listen(int port)
{
    int listenfd;
    listenfd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(port);

    int on = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    int rt1 = bind(listenfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (rt1 < 0)
    {
        error(1, errno, "bind failed ");
    }

    int rt2 = listen(listenfd, LISTENQ);
    if (rt2 < 0)
    {
        error(1, errno, "listen failed ");
    }

    signal(SIGPIPE, SIG_IGN);

    return listenfd;
}

int tcp_nonblocking_server_listen(int port)
{
    int listenfd;
    listenfd = socket(AF_INET, SOCK_STREAM, 0);

    make_nonblocking(listenfd);

    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(port);

    int on = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    int rt1 = bind(listenfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (rt1 < 0)
    {
        error(1, errno, "bind failed ");
    }

    int rt2 = listen(listenfd, LISTENQ);
    if (rt2 < 0)
    {
        error(1, errno, "listen failed ");
    }

    signal(SIGPIPE, SIG_IGN);

    return listenfd;
}

void make_nonblocking(int fd)
{
    fcntl(fd, F_SETFL, O_NONBLOCK);
}

struct TCPServer *tcp_server_init(struct event_loop *eloop, struct acceptor *acceptor,
                                  connection_completed_callback_t connection_completed_callback,
                                  message_callback_t message_callback,
                                  write_completed_callback_t write_completed_callback,
                                  connection_closed_callback_t connection_closed_callback,
                                  int thread_num)
{
    struct TCPServer *server = malloc(sizeof(struct TCPServer));
    server->eloop = eloop;
    server->acceptor = acceptor;
    server->connection_completed_callback = connection_completed_callback;
    server->message_callback = message_callback;
    server->write_completed_callback = write_completed_callback;
    server->connection_closed_callback = connection_completed_callback;
    server->thread_num = thread_num;
    server->pool = thread_pool_new(eloop, thread_num);
    server->data = NULL;

    return server;
}

int handle_connection_established(void *data)
{
    struct TCPServer *server = (struct TCPServer *)data;
    struct acceptor *acceptor = server->acceptor;
    int listenfd = acceptor->listen_fd;

    struct sockaddr_in client_addr;
    socklen_t client_len = sizeof(client_addr);
    int connected_fd = accept(listenfd, (struct sockaddr *)&client_addr, &client_len);
    make_nonblocking(connected_fd);

    struct event_loop *eloop = thread_pool_get_loop(server->pool);
    struct tcp_connection *conn = tcp_connection_new(connected_fd, eloop,
                                                     server->connection_completed_callback, server->connection_closed_callback,
                                                     server->message_callback, server->write_completed_callback);
    if (server->data != NULL)
    {
        conn->data = server->data;
    }
    return 0;
}

void tcp_server_start(struct TCPServer *server)
{
    struct acceptor *acceptor = server->acceptor;
    struct event_loop *eloop = server->eloop;

    // 开启多个线程
    thread_pool_start(server->pool);

    // acceptor 线程
    struct channel *chan = channel_new(acceptor->listen_fd, EVENT_READ, handle_connection_established,
                                       NULL, server);

    event_loop_add_channel_event(eloop, chan->fd, chan);
}

void tcp_server_set_data(struct TCPServer *server, void *data)
{
    if (data != NULL)
    {
        server->data = data;
    }
}